<?php
/**
 ***********************************************************************************************************************
 * 访问记录
 ***********************************************************************************************************************
 */
namespace soa\firewall\model;

/**
 * Class access
 * @package soa\firewall\model
 */
class access
{
    /**
     * 访问记录使用Redis Hash表存储
     */
    const RECORD_PREFIX = 'firewall_access_record_';

    /**
     * 客户端访问量使用Redis zset有序集合进行排序 展示出每日高访问量IP
     */
    const DANGER_KEY = 'firewall_acces_danger';

    /**
     * 添加当前用户访问信息
     * @param $ip
     * @param string $client_ident
     * @return bool
     */
    public function add($ip, $client_ident = '')
    {
        // 检测当前客户信息是否合法
        if(empty($ip) || !is_string($ip)) {
            return false;
        }

        // 获取历史访问记录
        $record = $this->getRecord($ip);
        // 设置时间
        $now = time();
        $data['update_at'] = $now;

        // 记录IP级别的访问记录
        $data['daily'] = empty($record['daily']) ? 1 : (intval($record['daily']) + 1);
        // 清空过期访问记录
        if(empty($record['update_at']) || date('d', $record['update_at']) != date('d')) {
            $data['daily'] = 1;
        }
        $data['hourly'] = empty($record['hourly']) ? 1 : (intval($record['hourly']) + 1);
        // 清空过期访问记录
        if(empty($record['update_at']) || date('H', $record['update_at']) != date('H')) {
            $data['hourly'] = 1;
        }

        // 记录客户端级别的访问记录
        if(!empty($client_ident) && is_string($client_ident)) {
            $data[$client_ident . '_daily'] = empty($record[$client_ident . '_daily']) ? 1 : (intval($record[$client_ident . '_daily']) + 1);
            // 清空过期访问记录
            if(empty($record['update_at']) || date('d', $record['update_at']) != date('d')) {
                $data[$client_ident . '_daily'] = 1;
            }
            $data[$client_ident . '_hourly'] = empty($record[$client_ident . '_hourly']) ? 1 : (intval($record[$client_ident . '_hourly']) + 1);
            // 清空过期访问记录
            if(empty($record['update_at']) || date('H', $record['update_at']) != date('H')) {
                $data[$client_ident . '_hourly'] = 1;
            }
        }

        // 存储访问记录
        $key = md5(self::RECORD_PREFIX . $ip);
        $this->getRedisInstance()->hMset($key, $data);
        // 如果是当天第一次访问，设置缓存过期时间  当天数发生变化时 就更新，防止设置过期时间失效
        if(empty($record['update_at']) || date('d', $record['update_at']) != date('d')) {
            // 次日零点过期 仅仅记录一天数据 减少redis内存占用
            $expireat = strtotime('+1 day', strtotime(date('Y-m-d')));
            $this->getRedisInstance()->expireat($key, $expireat);
        }

        return $data;
    }

    /**
     * 添加危险IP 即超过频率限制的IP
     * @param $ip
     * @param $data
     * @return bool
     */
    public function addDanger($ip, $data)
    {
        return $this->getRedisInstance()->hset(md5(self::DANGER_KEY), $ip, json_encode($data));
    }

    /**
     * @return array
     */
    public function getDanger()
    {
        return $this->getRedisInstance()->hGetAll(md5(self::DANGER_KEY));
    }

    /**
     * 获取当前IP的历史访问信息
     * @param $ip
     * @return array|bool
     */
    public function getRecord($ip)
    {
        // 检测当前客户信息是否合法
        if(empty($ip) || !is_string($ip)) {
            return false;
        }
        return $this->getRedisInstance()->hGetAll(md5(self::RECORD_PREFIX . $ip));
    }

    /**
     * @var \artisan\cache\cacheDriver
     */
    private $redisInstance;

    /**
     * @return \artisan\cache\cacheDriver
     */
    private function getRedisInstance()
    {
        if(empty($this->redisInstance)) {
            $this->redisInstance = \artisan\cache::connect('firewall');
        }
        return $this->redisInstance;
    }
}